﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using FaceSharp.Api;
using FaceSharp.Api.RestApi;
using FaceSharp.Api.Types;
using FaceSharp.MVC.Code.Common;
using FaceSharp.MVC.Code.Extensions;
using FaceSharp.MVC.Code.Facebook;
using FaceSharp.MVC.Filters;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;


namespace FaceSharp.MVC.Areas.FacebookApp.Controllers
{
    [FacebookGraphApiFilter]
    public class AdvancedController : Controller
    {
        public class SimpleUser
        {
            public string Name { get; set; }
            public long Id { get; set; }
            public IList<Like> Likes { get; set; }
        }

        public SimpleUser ParseUser(JToken jObject)
        {
            var likes = new List<Like>();
            var simpleUser = new SimpleUser()
            {
                Id = jObject["id"].Value<long>(),
                Name = jObject["name"].Value<string>()
            };
            var likeData = ((JToken)jObject["likes"]);
            if (likeData != null)
            {
                var jData = (JArray)likeData["data"];
                if (jData != null)
                {
                    likes = (from like in jData
                             select new Like()
                             {
                                 Id = long.Parse((string)like["id"]),
                                 Name = (string)like["name"],
                                 Category = (string)like["category"],
                                 CreatedTime = (string)like["created_time"]
                             }).ToList();
                }
            }
            simpleUser.Likes = likes;
            return simpleUser;
        }

        public ActionResult FriendsLikes()
        {
            var fbApi = new FacebookApi();

            // me/friends/?fields=name,idlikes
            var results = fbApi.GraphApi.MakeGraphRequest("me", "friends", new List<string> { "name,id,likes" });
            var parsedResults = ((JArray)results["data"]).Select(ParseUser).ToList();

            var myResults = parsedResults
                .SelectMany(x => x.Likes)
                .GroupBy(x => x.Id)
                .Select(x => new KeyValuePair<long,long>(x.Key, x.Count()))
                .OrderByDescending(x => x.Value)
                .ToList();

            return View(myResults);
        }


        public string NbaFanPageData(FacebookApi fbApi)
        {
            const string fanPagesFql =
               "SELECT page_id, name, description, pic_small, pic_big, pic_square, pic, pic_large, page_url, fan_count, website, general_info, checkins FROM page WHERE page_id IN (108078252552885,8725012666,53844953429,37152881613,69048043277,16555485460,98534743609,71284685294,47657117525,17729777319,43267052995,123393184395601,144917055340,37749707254,284300559123,19180542816,8373804260,108295665865810,186563232926,21410634615,159957123994,74947776908,52974817515,126192290730203,20214931136,96222644025,25781101980,29958461768,50428070267,29805028740)";

            var pageResults = fbApi.RestApi.ExecuteFql(fanPagesFql, RestBase.ResponseFormat.Json, false);

            return pageResults;
        }

        public Page ConvertToPage(JToken jObj)
        {
            var page = new Page();
            if(jObj != null)
            {
                page.PageId = jObj.Value<long>("page_id");
                page.Name = jObj.Value<string>("name");
                page.Description = jObj.Value<string>("description");
                page.PicSmall = jObj.Value<string>("pic_small");
                page.PicBig = jObj.Value<string>("pic_big");
                page.PicSquare = jObj.Value<string>("pic_square");
                page.PageUrl = jObj.Value<string>("page_url");
                page.FanCount = jObj.Value<long>("fan_count");
                page.WebSite = jObj.Value<string>("website");
                page.Generalinfo = jObj.Value<string>("general_info");
                page.Checkins = jObj.Value<long>("checkins");
            }
            return page;
        }

        public string NbaFanData(FacebookApi fbApi)
        {
            const string fqlQuery =
                "SELECT uid, page_id, type FROM page_fan WHERE page_id IN (108078252552885,8725012666,53844953429,37152881613,69048043277,16555485460,98534743609,71284685294,47657117525,17729777319,43267052995,123393184395601,144917055340,37749707254,284300559123,19180542816,8373804260,108295665865810,186563232926,21410634615,159957123994,74947776908,52974817515,126192290730203,20214931136,96222644025,25781101980,29958461768,50428070267,29805028740) AND uid IN (SELECT uid2 FROM friend WHERE uid1 = me())";

           
            var results = fbApi.RestApi.ExecuteFql(fqlQuery, RestBase.ResponseFormat.Json, false);

   

            return results;
        }

        public class Page
        {
            public long PageId { get; set; }
            public string Name { get; set; }
            public string UserName { get; set; }
            public string Description { get; set; }
            public string PicSmall { get; set; }
            public string PicBig { get; set; }
            public string PicSquare { get; set; }
            public string Pic { get; set; }
            public string PicLarge { get; set; }
            public string PageUrl { get; set; }
            public long FanCount { get; set; }
            public string WebSite { get; set; }
            public string Generalinfo { get; set; }
            public long Checkins { get; set; }
            public IList<User> Fans { get; set; }
        }

        public class NbaFanDataRelationship
        {
            public long Uid { get; set; }
            public long PageId { get; set; }
        }
        public NbaFanDataRelationship ConvertNbaFanData(JToken jObj)
        {
            var results = new NbaFanDataRelationship();
            if(jObj != null)
            {
                results.Uid = jObj.Value<long>("uid");
                results.PageId = jObj.Value<long>("page_id");
            }
            return results;
        }

        public JsonResult NbaFansData()
        {
            var fbApi = new FacebookApi();

            var pages = NbaFanSummary(fbApi).OrderByDescending(x => x.Fans.Count).ThenByDescending(x => x.FanCount);

            return Json(pages, JsonRequestBehavior.AllowGet);
        }


        public ActionResult NbaFans()
        {
            var fbApi = new FacebookApi();

            var pages = NbaFanSummary(fbApi).OrderByDescending(x => x.Fans.Count).ThenByDescending(x => x.FanCount);


            return View(pages);
        }

        public IEnumerable<Page> NbaFanSummary(FacebookApi fbApi)
        {
            var jsonFanData = JArray.Parse(NbaFanData(fbApi));
            IList<NbaFanDataRelationship> fanData = null;
            if (jsonFanData != null)
            {
                fanData = jsonFanData.Select(ConvertNbaFanData).ToList();
            }
            var jsonFanPageData = JArray.Parse(NbaFanPageData(fbApi));
            IList<Page> pages = null;
            if (jsonFanPageData != null)
            {
                pages = jsonFanPageData.Select(ConvertToPage).ToList();
            }
            var friends = fbApi.User.GetFriends().ToList();

            if (pages != null && friends.Count > 0 && fanData != null)
                foreach (var page in pages)
                {
                    page.Fans = (from f in friends
                                 join fd in fanData on f.Id equals fd.Uid
                                 where fd.PageId == page.PageId
                                 select f).ToList();
                }
            return pages;
        }

        /// <summary>
        /// Returns friends tagged in your Photos,Videos,Posts, or whom have liked or commented on your stream/status/albums/photos/videos/notes or links.
        /// Score is calculated for each action where
        ///  - YOU liked an object in your FRIENDS stream/status/albums/photos/videos/notes or links
        ///  - YOU commented on an object in your FRIENDS stream/status/albums/photos/videos/notes or links
        /// </summary>.
        /// <returns></returns>
        public ActionResult ClosestFriends()
        {
            ViewBag.Title = "Closest Friends";
            var facebookApi = new FacebookApi();

            var friends = facebookApi.User.GetFriends();

            const string objectsYouCommentedOnFromYourFriends = "SELECT fromid, '{0}' FROM comment WHERE fromid = me() AND (object_id IN (SELECT post_id FROM stream WHERE source_id = {0}) OR object_id IN (SELECT status_id FROM status WHERE uid = {0}) OR object_id IN (SELECT object_id FROM album WHERE owner = {0}) OR object_id IN (SELECT object_id FROM photo WHERE aid in (SELECT aid FROM album WHERE owner = {0})) OR object_id IN (SELECT vid FROM video WHERE owner = {0}) OR object_id IN (SELECT note_id FROM note WHERE uid = {0}) OR object_id IN (SELECT link_id FROM link WHERE owner = {0}))";
            const string objectsYouLikedFromYourFriends = "SELECT user_id, '{0}' FROM like WHERE user_id = me() AND (object_id IN (SELECT post_id FROM stream WHERE source_id = {0}) OR object_id IN (SELECT status_id FROM status WHERE uid = {0}) OR object_id IN (SELECT object_id FROM album WHERE owner = {0}) OR object_id IN (SELECT object_id FROM photo WHERE aid in (SELECT aid FROM album WHERE owner = {0})) OR object_id IN (SELECT vid FROM video WHERE owner = {0}) OR object_id IN (SELECT note_id FROM note WHERE uid = {0}) OR object_id IN (SELECT link_id FROM link WHERE owner = {0}))";

            var fqlQueries = new List<dynamic>();
     
            foreach (var friend in friends)
            {
                fqlQueries.Add(new { query = string.Format(objectsYouCommentedOnFromYourFriends, friend.Id), key = "anon", pointValue=1m });
                fqlQueries.Add(new { query = string.Format(objectsYouLikedFromYourFriends, friend.Id), key = "anon", pointValue=.5m });
            }

            var topFriendUids = new Hashtable();
            friends.ForEach(x => topFriendUids.Add((long)x.Id, 0m));

            IFacebookCore myFacebooCoreAsync = new FacebookCoreAsync(new FacebookCore());
            Parallel.ForEach(fqlQueries, queryDefinition =>
            {
                var key = (string)queryDefinition.key;
                var pointValue = (decimal)queryDefinition.pointValue;
                string resultsJson = new FacebookApi(myFacebooCoreAsync).RestApi.ExecuteFql(queryDefinition.query, RestBase.ResponseFormat.Json, false);
                if (!string.IsNullOrEmpty(resultsJson) && resultsJson.Trim().Length > 5 && !resultsJson.Contains("error_code"))
                {
                    IEnumerable<dynamic> results = JsonConvert.DeserializeObject<IList<ExpandoObject>>(resultsJson);

                    results.ForEach(x =>
                    {
                        var fqlResult = x as IDictionary<string, object>;
                        var uid = long.Parse(fqlResult[key].ToString());
                        if (fqlResult != null && topFriendUids.ContainsKey(uid))
                        {
                            topFriendUids[uid] = ((decimal)(topFriendUids[uid])) + pointValue;
                        }
                    });
                }
            });

            var topFriends = new List<dynamic>();

            foreach (var friend in friends)
            {
                dynamic friendDetail = new ExpandoObject();
                friendDetail.Name = friend.Name;
                friendDetail.Score = topFriendUids[friend.Id];
                friendDetail.Id = friend.Id;
                topFriends.Add(friendDetail);
            }

            return View("CloseFriends",topFriends.OrderByDescending(x => x.Score).AsEnumerable());
       }

        /// <summary>
        /// Returns friends tagged in your Photos,Videos,Posts, or whom have liked or commented on your stream/status/albums/photos/videos/notes or links.
        /// Score is calculated for each action by individual 
        ///  - liked an object in your stream/status/albums/photos/videos/notes or links
        ///  - commented on an object in your stream/status/albums/photos/videos/notes or links
        ///  - tagged in your photos
        ///  - tagged in your videos
        ///  - tagged in your posts
        /// </summary>
        /// <returns></returns>
        public ActionResult CloserFriends()
        {
            ViewBag.Title = "Closer Friends";
            var facebookApi = new FacebookApi();

            

            const string friendsWhoCommentedOnYourObjects = "SELECT fromid FROM comment WHERE object_id IN (SELECT post_id FROM stream WHERE source_id = me()) OR object_id IN (SELECT status_id FROM status WHERE uid = me()) OR object_id IN (SELECT object_id FROM album WHERE owner = me()) OR object_id IN (SELECT object_id FROM photo WHERE aid in (SELECT aid FROM album WHERE owner = me())) OR object_id IN (SELECT vid FROM video WHERE owner = me()) OR object_id IN (SELECT note_id FROM note WHERE uid = me()) OR object_id IN (SELECT link_id FROM link WHERE owner = me())";
            const string friendsWhoLikedYourObjects = "SELECT user_id FROM like WHERE object_id IN (SELECT post_id FROM stream WHERE source_id = me()) OR object_id IN (SELECT status_id FROM status WHERE uid = me()) OR object_id IN (SELECT object_id FROM album WHERE owner = me()) OR object_id IN (SELECT object_id FROM photo WHERE aid in (SELECT aid FROM album WHERE owner = me())) OR object_id IN (SELECT vid FROM video WHERE owner = me()) OR object_id IN (SELECT note_id FROM note WHERE uid = me()) OR object_id IN (SELECT link_id FROM link WHERE owner = me())";
            const string friendsWhoWereTaggedInYourPhotos = "SELECT subject FROM photo_tag WHERE pid IN ( SELECT pid FROM photo WHERE aid in (SELECT aid FROM album WHERE owner = me()))";
            const string friendsWhoWereTaggedInYourVideos = "SELECT subject FROM video_tag WHERE vid IN ( SELECT vid FROM video WHERE owner = me())";
            const string friendsWhoWereTaggedInYourPosts = "SELECT target_id FROM stream_tag WHERE actor_id = me()";

            var fqlQueries = new List<dynamic>()
                                 {
                                     new {query = friendsWhoCommentedOnYourObjects, key = "fromid", pointValue=1m},
                                     new {query = friendsWhoLikedYourObjects, key = "user_id", pointValue=.5m},
                                     new {query = friendsWhoWereTaggedInYourPhotos, key = "subject", pointValue=.4m},
                                     new {query = friendsWhoWereTaggedInYourVideos, key = "subject", pointValue=.4m},
                                     new {query = friendsWhoWereTaggedInYourPosts, key = "target_id", pointValue=1.5m},
                                 };

            var topFriendUids = new Hashtable();
            var friends = facebookApi.User.GetFriends();
            friends.ForEach(x => topFriendUids.Add((long)x.Id, 0m));

            IFacebookCore myFacebooCoreAsync = new FacebookCoreAsync(new FacebookCore());
            Parallel.ForEach(fqlQueries, queryDefinition =>
            {
                var key = (string)queryDefinition.key;
                var pointValue = (decimal)queryDefinition.pointValue;
                string resultsJson = new FacebookApi(myFacebooCoreAsync).RestApi.ExecuteFql(queryDefinition.query, RestBase.ResponseFormat.Json, false);
                if (!string.IsNullOrEmpty(resultsJson) && resultsJson.Trim().Length > 5 && !resultsJson.Contains("error_code"))
                {
                    IEnumerable<dynamic> results = JsonConvert.DeserializeObject<IList<ExpandoObject>>(resultsJson);

                    results.ForEach(x =>
                                        {
                                            var fqlResult = x as IDictionary<string, object>;
                                            if (fqlResult != null && topFriendUids.ContainsKey(fqlResult[key]))
                                            {
                                                topFriendUids[fqlResult[key]] = ((decimal)(topFriendUids[fqlResult[key]])) + pointValue;
                                            }
                                        });
                }
            });

            var topFriends = new List<dynamic>();

            foreach (var friend in friends)
            {
                dynamic friendDetail = new ExpandoObject();
                friendDetail.Name = friend.Name;
                friendDetail.Score = topFriendUids[friend.Id];
                friendDetail.Id = friend.Id;
                topFriends.Add(friendDetail);
            }

            return View("CloseFriends", topFriends.OrderByDescending(x => x.Score).AsEnumerable());
        }


        /// <summary>
        /// Returns friends tagged in your Photos,Videos,Posts, or whom have liked or commented on your stream/status/albums/photos/videos/notes or links.
        /// Score is calculated in groups of activites as whether or not an individual was included: (max of 5 points per friend)
        ///  - liked an object in your stream/status/albums/photos/videos/notes or links
        ///  - commented on an object in your stream/status/albums/photos/videos/notes or links
        ///  - tagged in your photos
        ///  - tagged in your videos
        ///  - tagged in your posts
        /// </summary>
        /// <returns></returns>
        public ActionResult CloseFriends()
        {
            ViewBag.Title = "Close Friends";
            var facebookApi = new FacebookApi();
            var friendDetails = new List<dynamic>();

            var friends = facebookApi.User.GetFriends();

            const string friendsWhoCommentedOnYourObjects = "SELECT fromid FROM comment WHERE object_id IN (SELECT post_id FROM stream WHERE source_id = me()) OR object_id IN (SELECT status_id FROM status WHERE uid = me()) OR object_id IN (SELECT object_id FROM album WHERE owner = me()) OR object_id IN (SELECT object_id FROM photo WHERE aid in (SELECT aid FROM album WHERE owner = me())) OR object_id IN (SELECT vid FROM video WHERE owner = me()) OR object_id IN (SELECT note_id FROM note WHERE uid = me()) OR object_id IN (SELECT link_id FROM link WHERE owner = me())";
            const string friendsWhoLikedYourObjects = "SELECT user_id FROM like WHERE object_id IN (SELECT post_id FROM stream WHERE source_id = me()) OR object_id IN (SELECT status_id FROM status WHERE uid = me()) OR object_id IN (SELECT object_id FROM album WHERE owner = me()) OR object_id IN (SELECT object_id FROM photo WHERE aid in (SELECT aid FROM album WHERE owner = me())) OR object_id IN (SELECT vid FROM video WHERE owner = me()) OR object_id IN (SELECT note_id FROM note WHERE uid = me()) OR object_id IN (SELECT link_id FROM link WHERE owner = me())";
            const string friendsWhoWereTaggedInYourPhotos = "SELECT subject FROM photo_tag WHERE pid IN ( SELECT pid FROM photo WHERE aid in (SELECT aid FROM album WHERE owner = me()))";
            const string friendsWhoWereTaggedInYourVideos = "SELECT subject FROM video_tag WHERE vid IN ( SELECT vid FROM video WHERE owner = me())";
            const string friendsWhoWereTaggedInYourPosts = "SELECT target_id FROM stream_tag WHERE actor_id = me()";

            var pre = "SELECT uid FROM user WHERE uid IN ( ";
            var post = " ) AND uid IN (SELECT uid1 FROM friend WHERE uid2 = me())";

            var queries = new List<string>
                              {
                                  {pre + friendsWhoCommentedOnYourObjects + post},
                                  {pre + friendsWhoLikedYourObjects + post},
                                  {pre + friendsWhoWereTaggedInYourPhotos + post},
                                  {pre + friendsWhoWereTaggedInYourVideos + post},
                                  {pre + friendsWhoWereTaggedInYourPosts + post}
                              };

            var topFriendUids = new Hashtable();
            
            friends.ForEach(x => topFriendUids.Add((long)x.Id, 0));

            IFacebookCore myFacebooCoreAsync = new FacebookCoreAsync(new FacebookCore());
            Parallel.ForEach(queries, query =>
            {
                var resultsJson = new FacebookApi(myFacebooCoreAsync).RestApi.ExecuteFql(query, RestBase.ResponseFormat.Json, false);
                if (!string.IsNullOrEmpty(resultsJson) && resultsJson.Trim().Length > 5 && !resultsJson.Contains("error_code"))
                {
                    IEnumerable<dynamic> results = JsonConvert.DeserializeObject<IList<ExpandoObject>>(resultsJson);
                    results.ForEach(x => topFriendUids[(long)x.uid] = ((int)(topFriendUids[(long)((dynamic)x).uid])) + 1);
                }
            });

            var topFriends = new List<dynamic>();

            foreach (var friend in friends)
            {
                dynamic friendDetail = new ExpandoObject();
                friendDetail.Name = friend.Name;
                friendDetail.Score = topFriendUids[friend.Id];
                friendDetail.Id = friend.Id;
                topFriends.Add(friendDetail);
            }

            return View("CloseFriends",topFriends.OrderByDescending(x => x.Score).AsEnumerable());
        }


        public ActionResult SignificantOthers()
        {
            var facebookApi = new FacebookApi();

            var coupleQuery =
                "select significant_other_id, uid from user where uid in (select uid2 from friend where uid1 = " + facebookApi.User.GetCurrentUserId() + ") and significant_other_id > 0";
            var uidsQuery =
                "select pic_big_with_logo, name, uid from user where uid in ( select significant_other_id from user where uid in (select uid2 from friend where uid1 = " + facebookApi.User.GetCurrentUserId() + ") and significant_other_id <> '') OR uid in ( select uid from user where uid in (select uid2 from friend where uid1 = " + facebookApi.User.GetCurrentUserId() + ") and significant_other_id <> '')";

            IEnumerable<dynamic> coupleResults = JsonConvert.DeserializeObject<IList<ExpandoObject>>(facebookApi.RestApi.ExecuteFql(coupleQuery, RestBase.ResponseFormat.Json, false));
            IEnumerable<dynamic> uidResults = JsonConvert.DeserializeObject<IList<ExpandoObject>>(facebookApi.RestApi.ExecuteFql(uidsQuery, RestBase.ResponseFormat.Json, false));

            var uids = new HashSet<long>();
            var coupleDetails = new List<dynamic>();

            foreach (var couple in coupleResults)
            {
                var friendId = (long)couple.uid;
                var sigOtherId = (long)couple.significant_other_id;
                if(!uids.Contains((long)sigOtherId))
                {
                    uids.Add((long)friendId); // only want to list the relationship once

                    dynamic friend = uidResults.Where(x => x.uid == friendId).First();
                    dynamic sigOther = uidResults.Where(x => x.uid == sigOtherId).First();

                    if (friend != null)
                    {
                        dynamic coupleDetail = new ExpandoObject();
                        coupleDetail.FriendName = friend.name;
                        coupleDetail.FriendPhoto = friend.pic_big_with_logo;

                        coupleDetail.SigOtherName = sigOther.name;
                        coupleDetail.SigOtherPhoto = sigOther.pic_big_with_logo;
                        coupleDetails.Add(coupleDetail);
                    }                    
                }
            }

            return View(coupleDetails);
        }

    }
}
